{
 "cells": [
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "## 简答题",
   "id": "900a6e19bb0fa847"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "1. TensorFlow 是否可以简单替代 NumPy？两者之间的主要区别是什么？\n",
    "不能简单替代，numpy趋向于python库，而前者偏向于构建和训练神经网络\n",
    "2. 使用 `tf.range(10)` 和 `tf.constant(np.arange(10))` 是否会得到相同的结果？\n",
    "数值结果都相同，但一个是直接创建tf类型的张量，另一个是转换\n",
    "3. 可以通过编写函数或继承 `tf.keras.losses.Loss` 来定义自定义损失函数。两种方法分别应该在什么时候使用？\n",
    "函数适合定义逻辑简单的函数，后者适合逻辑复杂的场景，需要保存\n",
    "4. 可以直接在函数中定义自定义指标或采用 `tf.keras.metrics.Metric` 子类。两种方法分别应该在什么时候使用？\n",
    "函数适用简单无状态指标，继承使用场景需要跨批次累计计算的指标\n",
    "5. 什么时候应该自定义层而不是自定义模型？\n",
    "自定义层：当需要封装可复用的计算单元（如特殊卷积、注意力机制）时，层是模型的 “积木”，专注于张量的转换逻辑（输入→输出）。\n",
    "例如：实现一个带掩码的全连接层、自定义激活函数的卷积层。\n",
    "自定义模型：当需要定义完整的网络结构（层的组合方式、前向传播流程）时，模型是层的 “组合体”，还可包含训练配置（优化器、损失函数）。\n",
    "例如：组合多个自定义层形成一个完整的分类模型、定义多输入 / 多输出的复杂网络。\n",
    "核心区别：层是 “局部计算单元”，模型是 “整体计算流程”。\n",
    "6. 有哪些示例需要编写自定义训练循环？\n",
    "非标准训练流程：如对抗训练（GAN 的生成器 / 判别器交替更新）、元学习（动态调整损失权重）。\n",
    "自定义梯度处理：如梯度裁剪、梯度累积（解决显存不足）、修改反向传播梯度。\n",
    "复杂评估逻辑：训练中需要实时计算非标准指标（如每步更新后执行特定验证流程）。\n",
    "多任务 / 多优化器：同一模型中不同层使用不同优化器（如迁移学习中冻结部分层）。\n",
    "与外部系统交互：训练中需要实时读取数据库数据、动态调整超参数。\n",
    "7. 自定义 Keras 组件中可以包含任意 Python 代码，还是必须转换为 TF 函数？\n",
    "可以包含任意 Python 代码，但需注意：\n",
    "未转换为tf.function的代码：在 Eager Execution 模式下可正常运行，但可能无法享受计算图优化（如自动并行、常量折叠），且部署时可能不支持（如 TensorFlow Lite、TensorRT）。\n",
    "转换为tf.function：可获得计算图优化，适合高性能部署，但需遵循 TensorFlow 的控制流（tf.cond、tf.while_loop）和数据类型规范。\n",
    "建议：核心计算逻辑（如call方法）用tf.function装饰，非性能关键的辅助代码可保留原生 Python。\n",
    "8. 如果要将函数转换为 TF 函数，应避免哪些主要模式？\n",
    "动态 Python 特性：如动态类型转换（int→str）、列表动态增删（list.append）、闭包捕获可变变量。\n",
    "非 TensorFlow 控制流：如for循环依赖动态长度（需用tf.range+tf.while_loop替代）、if条件依赖非张量变量。\n",
    "不可序列化对象：如文件句柄、数据库连接（tf.function需序列化计算图，无法保存这类对象）。\n",
    "修改外部状态：如全局变量、类属性（tf.function可能导致状态更新不一致）。\n",
    "依赖 Python 内置函数：如len()（应用于张量时需用tf.shape）、print（需用tf.print）。\n",
    "9. 何时需要创建动态 Keras 模型？ 如何动态创建Keras模型？为什么不是所有模型都动态化？\n",
    "适用场景：\n",
    "网络结构依赖输入数据（如输入长度动态变化的 NLP 模型）。\n",
    "条件分支依赖输入值（如根据输入特征动态选择不同子网络）。\n",
    "快速原型开发（无需预先定义输入形状，灵活调试）。"
   ],
   "id": "f0568883493d9ef7"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "## 编程题",
   "id": "9a282b6d9adda052"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "1. 实现一个执行层归一化的自定义层：\n",
    "    - a. `build()` 方法应定义两个可训练的权重 α 和 β，它们的形状均为 `input_shape[-1:]`，数据类型为 `tf.float32`。α 应该用 1 初始化，而 β 必须用 0 初始化。\n",
    "    - b. `call()` 方法应计算每个实例特征的均值和标准差。为此，可以使用 `tf.nn.moments(inputs, axes=-1, keepdims=True)`，它返回同一实例的均值 μ 和方差 σ²（计算方差的平方根便可获得标准差）。然后，该函数应计算并返回\n",
    "      $$\n",
    "      \\alpha \\otimes \\frac{(X-\\mu)}{(\\sigma+\\epsilon)} + \\beta\n",
    "      $$\n",
    "      其中 ε 是表示项精度的一个常量（避免被零除的小常数，例如 0.001）,$\\otimes$表示逐个元素相乘\n",
    "    - c. 确保自定义层产生与tf.keras.layers.LayerNormalization层相同（或几乎相同）的输出。\n",
    "\n",
    "2. 使用自定义训练循环训练模型来处理Fashion MNIST数据集（13_神经网络介绍 里用的数据集）：\n",
    "\n",
    "    - a.显示每个轮次、迭代、平均训练损失和每个轮次的平均精度（在每次迭代中更新），以及每个轮次结束时的验证损失和精度。\n",
    "    - b.尝试对上面的层和下面的层使用具有不同学习率的不同优化器。"
   ],
   "id": "5cd3d7096f87afd"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "import tensorflow as tf\n",
    "from tensorflow.keras.layers import Layer\n",
    "\n",
    "class CustomLayerNormalization(Layer):\n",
    "    def __init__(self, epsilon=0.001, **kwargs):\n",
    "        super(CustomLayerNormalization, self).__init__(** kwargs)\n",
    "        self.epsilon = epsilon  # 避免被零除的小常数\n",
    "\n",
    "    def build(self, input_shape):\n",
    "        # 定义可训练的权重α和β，形状为输入的最后一个维度\n",
    "        self.alpha = self.add_weight(\n",
    "            name='alpha',\n",
    "            shape=input_shape[-1:],\n",
    "            dtype=tf.float32,\n",
    "            initializer=tf.ones_initializer(),  # α用1初始化\n",
    "            trainable=True\n",
    "        )\n",
    "        self.beta = self.add_weight(\n",
    "            name='beta',\n",
    "            shape=input_shape[-1:],\n",
    "            dtype=tf.float32,\n",
    "            initializer=tf.zeros_initializer(),  # β用0初始化\n",
    "            trainable=True\n",
    "        )\n",
    "        super(CustomLayerNormalization, self).build(input_shape)\n",
    "\n",
    "    def call(self, inputs):\n",
    "        # 计算均值和方差\n",
    "        mean, variance = tf.nn.moments(inputs, axes=-1, keepdims=True)\n",
    "\n",
    "        # 计算标准差\n",
    "        std = tf.sqrt(variance + self.epsilon)\n",
    "\n",
    "        # 执行层归一化计算\n",
    "        normalized = (inputs - mean) / std\n",
    "\n",
    "        # 应用缩放和平移\n",
    "        output = self.alpha * normalized + self.beta\n",
    "\n",
    "        return output\n",
    "\n",
    "    def compute_output_shape(self, input_shape):\n",
    "        return input_shape  # 输出形状与输入相同\n",
    "\n",
    "# 验证自定义层与TensorFlow内置层的一致性\n",
    "def test_layer_normalization():\n",
    "    # 创建测试输入\n",
    "    test_input = tf.random.normal(shape=(2, 3, 4), dtype=tf.float32)\n",
    "\n",
    "    # 自定义层\n",
    "    custom_ln = CustomLayerNormalization()\n",
    "    custom_output = custom_ln(test_input)\n",
    "\n",
    "    # TensorFlow内置层\n",
    "    tf_ln = tf.keras.layers.LayerNormalization(epsilon=0.001)\n",
    "    tf_output = tf_ln(test_input)\n",
    "\n",
    "    # 打印输出差异（应该非常小）\n",
    "    max_diff = tf.reduce_max(tf.abs(custom_output - tf_output))\n",
    "    print(f\"自定义层与内置层的最大差异: {max_diff.numpy()}\")\n",
    "\n",
    "    # 断言差异足够小\n",
    "    assert max_diff < 1e-5, \"自定义层与内置层输出差异过大\"\n",
    "\n",
    "# 运行测试\n",
    "test_layer_normalization()\n",
    "    "
   ],
   "id": "4cb9da9f5e9ea249"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
